package org.logstashplugins;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.Tuple;

/**
 * ClassName:RedisUtil <br/>
 * Function: Redis工具类. <br/>
 * Reason: Redis工具类. <br/>
 * Date: Jul 24, 2017 4:17:38 PM <br/>
 * 
 * @author zhuqiang
 * @version 1.0.0
 * @since JDK 1.7
 */
public class RedisUtil {
	/**
	 * jedisPool:连接池.
	 * 
	 * @since JDK 1.7
	 */
	private static volatile JedisPool jedisPool;
	
	public Jedis getConn() {
		return jedisPool.getResource();
	}

	public RedisUtil(String host, int port, String pass, JedisPoolConfig config) {
		if (StringUtils.isBlank(pass)) {
			jedisPool = new JedisPool(config, host, port, 2000);
		} else {
			jedisPool = new JedisPool(config, host, port, 2000, pass);
		}
	}

	/**
	 * NO_TIMEOUT:不设置超时时间.
	 * 
	 * @since JDK 1.7
	 */
	public static final int NO_TIMEOUT = 0;

	/**
	 * setex:保存字符串数据并设置超时时间. <br/>
	 * 保存字符串数据并设置超时时间.<br/>
	 *
	 * @author zhuqiang
	 * @param key     键
	 * @param seconds 超时时间为0时则不设置超时时间
	 * @param value   超时时间
	 * @return String
	 * @since JDK 1.7
	 */
	public String setex(String key, int seconds, String value) {
		// redis连接
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.setex(key, seconds, value);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * exists:判断key是否存在. <br/>
	 * 判断key是否存在.<br/>
	 *
	 * @author lhb
	 * @param key 键
	 * @return 是否存在
	 * @since JDK 1.7
	 */
	public boolean exists(String key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.exists(key);
		} catch (Exception e) {
			return false;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * hset:根据键设置hash表中的filed value. <br/>
	 * 根据键设置hash表中的filed value.<br/>
	 *
	 * @author lhb
	 * @param key   键
	 * @param field field
	 * @param value value
	 * @return 返回值
	 * @since JDK 1.7
	 */
	public Long hset(String key, String field, String value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hset(key, field, value);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * hdel:根据key field 删除hash表中值. <br/>
	 * 根据key field 删除hash表中值.<br/>
	 *
	 * @author lhb
	 * @param key   key
	 * @param field field
	 * @since JDK 1.7
	 */
	public Long hdel(String key, String field) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hdel(key, field);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * hdel:重载方法，根据key,fields数组批量删除哈希结构中字段. <br/>
	 * 重载方法，根据key,fields数组批量删除哈希结构中字段.<br/>
	 *
	 * @author atc
	 * @param key    主键
	 * @param fields 字段数组
	 * @since JDK 1.7
	 */
	public Long hdel(String key, String... fields) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hdel(key, fields);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * get:String表取值. <br/>
	 * String表取值.<br/>
	 *
	 * @author zhuqiang
	 * @param key 键
	 * @return String
	 * @since JDK 1.7
	 */
	public String get(String key) {
		// redis连接
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.get(key);
		} catch (Exception e) {
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * del:删除键. <br/>
	 * 适用于批量删除键.<br/>
	 *
	 * @author atc
	 * @param keys 要删除的键数组
	 * @return 返回删除的数量
	 * @since JDK 1.7
	 */
	public Long del(String... keys) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.del(keys);
		} catch (Exception e) {
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * getKey:(根据匹配条件获取key). <br/>
	 * 根据[匹配条件获取key.<br/>
	 *
	 * @author lhb
	 * @param pattern key
	 * @return List 返回值
	 * @since JDK 1.7
	 */
	public Set<String> keys(String pattern) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.keys(pattern);
		} catch (Exception e) {
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * mget:批量获取数据. <br/>
	 * 批量获取数据.<br/>
	 *
	 * @author zhuqiang
	 * @param keys 键
	 * @return List 结果
	 * @since JDK 1.7
	 */
	public List<String> mget(String... keys) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.mget(keys);
		} catch (Exception e) {
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * hgetAll:根据键 获取该键下的hash表. <br/>
	 * 根据键 获取该键下的hash表.<br/>
	 *
	 * @author lhb
	 * @param key 键
	 * @return Map 返回值
	 * @since JDK 1.7
	 */
	public Map<String, String> hgetAll(String key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hgetAll(key);
		} catch (Exception e) {
			return null;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * setKV:保存key ,value. <br/>
	 * 
	 * @author atc
	 * @param key   键
	 * @param value 值
	 * @return 成功返回ok
	 * @since JDK 1.7
	 */
	public String setKeyValue(String key, String value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.set(key, value);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hsetMap:保存哈希结构数据. <br/>
	 * 适用于哈希结构数据的保存.<br/>
	 *
	 * @author atc
	 * @param key           主键
	 * @param fieldValueMap 哈希结构
	 * @return 成功Ok
	 * @since JDK 1.7
	 */
	public String hsetMap(String key, Map<String, String> fieldValueMap) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hmset(key, fieldValueMap);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * publish:推入消息到redis消息通道. <br/>
	 * 推入消息到redis消息通道.<br/>
	 *
	 * @author zhuqiang
	 * @param channel 频道
	 * @param message 消息
	 * @since JDK 1.7
	 */
	public void publish(String channel, String message) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.publish(channel, message);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * subscribe:订阅redis消息通道. <br/>
	 * 订阅redis消息通道.<br/>
	 *
	 * @author zhuqiang
	 * @param jedisPubSub JedisPubSub对象
	 * @param channels    频道
	 * @since JDK 1.7
	 */
	public void subscribe(JedisPubSub jedisPubSub, String... channels) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.subscribe(jedisPubSub, channels);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * rpush:将字符串列表或单个字符串添加到指定列表. <br/>
	 * 将字符串列表或单个字符串添加到指定列表.<br/>
	 *
	 * @author atc
	 * @param key   列表名称
	 * @param value 要添加的字符串列表
	 * @since JDK 1.7
	 */
	public void rpush(String key, String... value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.rpush(key, value);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * llen:返回列表长度. <br/>
	 * 返回列表长度.<br/>
	 *
	 * @author atc
	 * @param key 要查找长度的KEY
	 * @return 列表的长度
	 * @since JDK 1.7
	 */
	public Long llen(String key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.llen(key);
		} catch (Exception e) {
			e.printStackTrace();
			return (long) 0;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * lrange:返回列表中的数据. <br/>
	 * 适用于Redis中数据类型为List.<br/>
	 *
	 * @author atc
	 * @param key   列表主键
	 * @param start 开始位置
	 * @param end   结束位置
	 * @return 列表对象
	 * @since JDK 1.7
	 */
	public List<String> lrange(String key, long start, long end) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.lrange(key, start, end);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * lrem:删除列表中的元素. <br/>
	 * 适用于List.<br/>
	 *
	 * @author atc
	 * @param key   要删除的列表
	 * @param count 为0，从头开始删除所有value值；为正数，从头开始删除value，count次；为负数，从尾部开始，删除value，|count|次
	 * @param value 要删除的值
	 * @return 删除的value个数
	 * @since JDK 1.7
	 */
	public long lrem(String key, long count, String value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.lrem(key, count, value);
		} catch (Exception e) {
			e.printStackTrace();
			return 0L;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hkeys:获取哈希表的所有字段名称. <br/>
	 * 适用于哈希表.<br/>
	 *
	 * @author atc
	 * @param hkeyName 表的名称
	 * @return 表中的所有字段的名称
	 * @since JDK 1.7
	 */
	public Set<String> hkeys(String hkeyName) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hkeys(hkeyName);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hlen:返回哈希表中字段的数量. <br/>
	 *
	 * @author atc
	 * @param key 表
	 * @return 哈希表中字段的个数
	 * @since JDK 1.7
	 */
	public Long hlen(String key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hlen(key);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * expire:设置Key超市时间. <br/>
	 *
	 * @author atc
	 * @param key     待设置超时时间的KEY
	 * @param seconds 待设置的超时间（单位为秒）
	 * @return 1：设置超时时间成功；0：该密钥已经具有关联的超时，或者KEY不存在
	 * @since JDK 1.7
	 */
	public long expire(String key, final int seconds) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.expire(key, seconds);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hexists:查看哈希表 key 中，给定域 field 是否存在. <br/>
	 *
	 * @author atc
	 * @param key   哈希表
	 * @param field 字段
	 * @return 存在与否
	 * @since JDK 1.8
	 */
	public boolean hexists(String key, String field) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.hexists(key, field);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hmget:批量获取哈希字段值. <br/>
	 *
	 * @author atc
	 * @param key    哈希KEy
	 * @param fields 字段
	 * @return 字段值列表(若值不存在返回的列表字段值为null)
	 * @since JDK 1.8
	 */
	public List<String> hmget(String key, String... fields) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			if (fields != null && fields.length > 0) {
				return jedis.hmget(key, fields);
			}
			return new ArrayList<>();
		} catch (Exception e) {
			e.printStackTrace();
			List<String> list = new ArrayList<>();
			for (int i = 0; i < fields.length; i++) {
				list.add(null);
			}
			return list;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * scan:遍历redis的键<br/>
	 * 适用于非阻塞式遍历key.<br/>
	 *
	 * @author atc
	 * @param cursor  游标
	 * @param pattern 使用glob风格的通配符,可参看https://blog.csdn.net/beyond__devil/article/details/60570158
	 * @param count   查询数据量（这个值标识扫描的键数量，并不是真正的会返回count键）
	 * @return ScanResult
	 * @since JDK 1.8
	 */
	public ScanResult<String> scan(String cursor, String pattern, int count) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			ScanParams params = new ScanParams();
			params.match(pattern);
			params.count(count);
			ScanResult<String> scanresult = jedis.scan(cursor, params);
			return scanresult;
		} catch (Exception e) {
			e.printStackTrace();
			List<String> list = new ArrayList<>();
			ScanResult<String> result = new ScanResult<>(cursor, list);
			return result;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * hscan:渐进式获取哈希结构的FIELD-VALUE. <br/>
	 *
	 * @author atc
	 * @param key     哈希表
	 * @param pattern 匹配的模式
	 * @param count   期望返回的数据量
	 * @param cursor  游标
	 * @return ScanResult
	 * @since JDK 1.8
	 */
	public ScanResult<Map.Entry<String, String>> hscan(final String key, final Integer count, String pattern,
			final String cursor) {
		try (Jedis jedis = jedisPool.getResource()) {
			ScanParams params = new ScanParams();
			params.match(pattern);
			params.count(count);
			ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(key, cursor, params);
			return scanResult;
		} catch (Exception e) {
			e.printStackTrace();
			List<Map.Entry<String, String>> list = new ArrayList<>();
			ScanResult<Map.Entry<String, String>> result = new ScanResult<>("0", list);
			return result;
		}
	}

	/**
	 * hincreBy:哈希结构计数器. <br/>
	 *
	 * @author atc
	 * @param key   哈希表
	 * @param field 字段
	 * @param value 增加的值
	 * @return 增加后的值
	 * @since JDK 1.8
	 */
	public long hincreBy(String key, String field, long value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Long incred = jedis.hincrBy(key, field, value);
			return incred;
		} catch (Exception e) {
			e.printStackTrace();
			return value;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * zrangeWithScores:查询ZSET中指定分数范围内的元素. <br/>
	 *
	 * @author atc
	 * @param key   ZSET的KEY
	 * @param start 最小分数
	 * @param end   最大分数
	 * @return start与end之间的元素SET
	 * @since JDK 1.8
	 */
	public Set<Tuple> zrangeWithScores(String key, long start, long end) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();

			Set<Tuple> sets = jedis.zrangeWithScores(key, start, end);
			return sets;
		} catch (Exception e) {
			e.printStackTrace();
			return new HashSet<>();
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * zrem:删除ZSET中数据. <br/>
	 *
	 * @author atc
	 * @param key     ZSET的KEY
	 * @param members 待删除的元素
	 * @return 删除的元素个数
	 * @since JDK 1.8
	 */
	public long zrem(String key, String... members) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Long long1 = jedis.zrem(key, members);
			return long1;
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	/**
	 * zcard:有序集合成员总数. <br/>
	 *
	 * @author atc
	 * @param key 有序集合KEY
	 * @return 有序集合成员总数
	 * @since JDK 1.8
	 */
	public long zcard(String key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Long long1 = jedis.zcard(key);
			return long1;
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	public long zadd(String key, double score, String member) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			Long long1 = jedis.zadd(key, score, member);
			return long1;
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

	public Set<String> zrangeByScore(String key, long min, long max) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			return jedis.zrangeByScore(key, min, max);
		} catch (Exception e) {
			e.printStackTrace();
			return new HashSet<>();
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}

}
